﻿package com.tools
{
	import com.tools.XmlLoader;
	import com.tools.SongLoader;
	import com.tools.notifications.XmlEvent;
	
	import flash.media.SoundChannel;
	import flash.media.SoundTransform;
	
	import flash.events.Event;
	import flash.events.ProgressEvent;
	
	import com.conf.PlayerSpec;
	import com.datasource.SongVO;
	
	/////////////////////////////////////////////////////////////////////////////////////////////////////////
	// SoundManager ::
	// - gestionnaire de lecture des sons
	/////////////////////////////////////////////////////////////////////////////////////////////////////////
	
	public class SoundManager
	{
		/////////////////////////////////////////////////////////////////////////////////////////////////////////
		// MEMBRES DE CLASSE
		/////////////////////////////////////////////////////////////////////////////////////////////////////////
		
		// implémentation du design pattern Singleton
		static private var _soundManager 	: SoundManager;
		
		// module de chargement
		private var _xmlLoader				: XmlLoader;
		
		// fichier xml contenant les informations sur les chansons
		private var _soundChannelXmlFile			: String 	= PlayerSpec.SONGLIST_REPOSITORY + PlayerSpec.SONGLIST_FILE + PlayerSpec.XML_FORMAT;
		
		// informations sur les fichiers sons
		private var list_sound				: Array 	= new Array();
		
		// conteneur média où l'on charge les sons + canal sonore
		static public var _songLoader		: SongLoader;
		private var _soundChannel			: SoundChannel = new SoundChannel();
		
		// l'état de disponibilité de la liste des chansons
		private var songListReady			: Boolean	= false;
		// l'état de disponibilité du son en cours
		private var currentSongReady		: Boolean	= false
		
		// index désignant le numéro de chanson en cours dans la liste des chansons
		private var current_index			: uint	 	= 0;
		
		// durée à laquelle le son doit être joué
		private var _position				: Number	= 0;
		
		// si la lecture d'un son est en cours
		private var _isPlaying				: Boolean	= false;
		
		// si la lecture a été mise en pause
		private var _isPaused				: Boolean	= false;
		
		/////////////////////////////////////////////////////////////////////////////////////////////////////////
		// METHODE STATIQUE : implémentation du design pattern Singleton
		/////////////////////////////////////////////////////////////////////////////////////////////////////////
		
		static public function getInstance( ) : SoundManager
		{
			if ( ! _soundManager )
			{
				_soundManager 	= new SoundManager( );
				// le fait de placer ce code ici est une fausse implémentation
				// du design pattern Singleton pour l'objet SongLoader
				_songLoader 	= new SongLoader( );
			}
			return _soundManager;
		}
		
		/////////////////////////////////////////////////////////////////////////////////////////////////////////
		// METHODES PUBLIQUES
		/////////////////////////////////////////////////////////////////////////////////////////////////////////
		
		// on commence à charger le xml qui contient les infos des chansons
		public function init ( ) : void
		{
			// si on recharge la skin on réinitialise la lecture du player
			if ( isPlaying )	isPlaying 	= false;
			if ( isPaused )		isPaused	= false;
			if ( _soundChannel ) _soundChannel.stop();
			
			songListReady = false;
			_xmlLoader = new XmlLoader( _soundChannelXmlFile, PlayerSpec.DISPATCH_SONGLIST_EVENT_TYPE );
			_xmlLoader.addEventListener( XmlEvent.LOAD_SONGLIST_XML_COMPLETE, disposeSongs );
		}
		
		// méthode appelée pour lancer la lecture d'un son
		public function playSong( ) : void
		{
			start_Song( _position );
		}
		
		// méthode appelée pour mettre le son en pause
		public function pauseSong( ) : void
		{
			if ( _songLoader.playingSong )
			{
				if ( isPaused )
						start_Song ( _position );
				else	freeze_Song( );
			}
		}
		
		// méthode appelée pour stopper la lecture d'un son
		public function stopSong( ) : void
		{
			stop_Song( );
		}
		
		// méthode appelée pour jouer le son suivant
		public function nextSong( pEvt : Event = null ) : void
		{
			var wasPlaying : Boolean = _songLoader.playingSong;
			
			stop_Song( );
			
			isPlaying = wasPlaying;
			
			if ( current_index < ( list_sound.length -1 ) )
				current_index ++;
			else
				current_index = 0;
				
			trace("NEXT : " + current_index );
			if ( pEvt )
				trace("NEXT pEvt : " + pEvt.type )
			
			initialize_Song( current_index );
		}
		
		// méthode appelée pour jouer le son précédent
		public function previousSong( ) : void
		{
			var wasPlaying : Boolean = _songLoader.playingSong;
			
			stop_Song( );
			
			isPlaying = wasPlaying;
			
			if ( current_index > 0 )
				current_index --;
			else
				current_index = list_sound.length -1;
				
			trace("PREVIOUS : " + current_index );
			
			initialize_Song( current_index );
		}
		
		/////////////////////////////////////////////////////////////////////////////////////////////////////////
		// METHODES PRIVEES
		/////////////////////////////////////////////////////////////////////////////////////////////////////////
		
		// on crée la liste des chansons à partir des infos du xml
		private function disposeSongs( pEvt : XmlEvent ) : void
		{
			var xml 	: XML 		= new XML ( pEvt.infos );
			var xmlList : XMLList 	= xml.*;
			var songVO	: SongVO;
			
			for each ( var node : XML in xmlList )
			{
				if ( node.localName() == PlayerSpec.NODE_SONG)
				{
					songVO = new SongVO(	node.@[ PlayerSpec.ATTRIBUTE_URL_SONG ],
											node.@[ PlayerSpec.ATTRIBUTE_TYPE_SONG ]
											);
					list_sound.push( songVO );
					trace( "///\nSongVO : " + node.@[ PlayerSpec.ATTRIBUTE_URL_SONG ] + "\n---" );
				}
				else throw new Error( "Le xml qui contient la liste de vos chansons est mal formé" );
			}
			current_index = 0;
			songListReady = true;
			initialize_Song( 0 );
		}
		
		// on précharge le son pour son écoute
		private function initialize_Song( i : uint = 0 ) : void
		{
			currentSongReady = false;
			var songUrl : String = PlayerSpec.SONGLIST_REPOSITORY + list_sound[i][ PlayerSpec.PROPERTY_TYPE_SONG ] + "/" + list_sound[i][ PlayerSpec.PROPERTY_URL_SONG ] + PlayerSpec.MP3_FORMAT;
			_songLoader.initUrl( songUrl );
			_songLoader.soundFactory.addEventListener( ProgressEvent.PROGRESS, check_Song );
		}
		
		// on écoute la progression du chargement pour déterminer lorsque le son peut être lancé
		private function check_Song( pEvt : ProgressEvent ) : void
		{
			trace( "* ProgressEvent -> " + pEvt.bytesLoaded );
			if( ( ! currentSongReady ) && _songLoader.songReady )
			{
				currentSongReady = true;
				if( isPlaying )
					start_Song( );
				_songLoader.soundFactory.removeEventListener( ProgressEvent.PROGRESS, check_Song );
			}			
		}
		
		// on démarre la lecture d'un son à un instant t
		private function start_Song( 
								  	position 		: Number		 = 0,
									loops 			: Number		 = 0,
									sndTransform	: SoundTransform = null
								  	) : void
		{
			if ( songListReady && currentSongReady )
			{
				if( ! isPaused )
				{
					position = 0;
					_soundChannel.stop();
					
				}
				_soundChannel = _songLoader.soundFactory.play( position, loops, sndTransform );
				
				if ( _soundChannel.hasEventListener( Event.SOUND_COMPLETE ) )
					_soundChannel.removeEventListener( Event.SOUND_COMPLETE, nextSong );
				_soundChannel.addEventListener( Event.SOUND_COMPLETE, nextSong );
				
				_songLoader.playingSong = true;
			}
			
			if ( ! isPlaying )
				isPlaying = true;
			
			if ( isPaused )
				isPaused = false;
		}
		
		// on stoppe la lecture d'un son en enregistrant l'instant t où le son est stoppé
		private function freeze_Song( ) : void
		{
			_position = _soundChannel.position;
			_soundChannel.stop();
			_songLoader.playingSong = true; // changed to TRUE
			isPlaying = false;
			isPaused = true;
		}
		
		// on stoppe complétement la lecture d'un son
		private function stop_Song( ) : void
		{
			if ( isPlaying || isPaused )
			{
				_position = 0;
				_soundChannel.stop();
				_songLoader.playingSong = false;
				isPlaying = false;
				isPaused = false;
			}
		}
		
		/////////////////////////////////////////////////////////////////////////////////////////////////////////
		// GETTER & SETTER
		/////////////////////////////////////////////////////////////////////////////////////////////////////////
		
		public function get isPlaying ( ) : Boolean
		{
			return _isPlaying;
		}
		
		public function set isPlaying ( val : Boolean ) : void
		{
			_isPlaying = val;
		}
		
		public function get isPaused ( ) : Boolean
		{
			return _isPaused;
		}
		
		public function set isPaused ( val : Boolean ) : void
		{
			_isPaused = val;
		}
	}	
}